package ci.web.util;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

public class JxHelper {
	
//	private static boolean isIndexName(String s){
//		return s.length()==1 && s.charAt(0)>='0'||s.charAt(0)<='9';
//	}
//	@SuppressWarnings({ "rawtypes"})
//	protected static Constructor makeProxy(Executable method, String basePath) {
//		Constructor ret = null;
//			boolean isPost = method.getAnnotation(LimitMethod.class)!=null && 
//					method.getAnnotation(LimitMethod.class).value()==HttpMethod.POST;
//			List<String> names = getParameterNames(method);
//			Class<?>[] types = method.getParameterTypes();
//			
//			for (int i = 0; i < types.length; i++) {
//				
//			}
//			
//		return ret;
//	}
//	
	
	
	
	
	
    /**
     * Weak map from
     */
    private static final WeakHashMap<Class<?>,Map<Executable, List<String>>> cache = new WeakHashMap<>();
    
    public static void clear(){
    	cache.clear();
    }
    public static String[] getParameterNamesArray(Executable m){
    	return getParameterNames(m).toArray(new String[m.getParameterCount()]);
    }
    public static List<String> getParameterNames(Method m){
        return getParameterNames(m.getDeclaringClass()).get(m);
    }
    public static List<String> getParameterNames(Constructor<?> m){
        return getParameterNames(m.getDeclaringClass()).get(m);
    }
    public static List<String> getParameterNames(Executable m){
        return getParameterNames(m.getDeclaringClass()).get(m);
    }
    
    public static Map<Executable, List<String>> getParameterNames(Class<?> clazz){
        Map<Executable, List<String>> m = cache.get(clazz);
        if(m==null || m.isEmpty()){
            m = asm_parse(clazz);
            cache.put(clazz, m);
        }
        return m;
    }
    
    /**
     * @param clazz
     * @return <{方法/构造方法}->[参数名数组]>
     */
    private static Map<Executable, List<String>> asm_parse(Class<?> clazz){
        final Map<Executable, List<String>> map = new HashMap<Executable, List<String>>();
        final HashMap<String, Executable> idxMap = new HashMap<String, Executable>();
        Constructor<?>[] carr = clazz.getConstructors();
        for(Constructor<?> c : carr){
            Type[] types = new Type[c.getParameterTypes().length];
            for (int j = 0; j < types.length; j++) {
                types[j] = Type.getType(c.getParameterTypes()[j]);
            }
            idxMap.put("<init>"+Type.getMethodDescriptor(Type.VOID_TYPE, types), c);
        }
        Method[] marr = clazz.getDeclaredMethods();
        for(Method m:marr){
            idxMap.put(m.getName()+Type.getMethodDescriptor(m), m);
        }
        ClassReader reader = new ClassReader(readClassFile(clazz));
        reader.accept(new ClassVisitor(Opcodes.ASM5) {
            @Override
            public MethodVisitor visitMethod(int access, String name, final String desc, String signature, String[] exceptions) {
                final Executable m = idxMap.get(name+desc);
                if(m==null){
                    return super.visitMethod(access, name, desc, signature, exceptions);
                }
                final boolean isStaticMethod = Modifier.isStatic(m.getModifiers());
                final int size = m.getParameterTypes().length;
                final ArrayList<String> list = new ArrayList<String>(size);
                map.put(m, list);
                MethodVisitor mv = new MethodVisitor(Opcodes.ASM5) {
                    @Override
                    public void visitLocalVariable(String name, String description, String signature, Label start, Label end, int index) {
                        if(isStaticMethod){
                            if(list.size()<size){
                                list.add(name);
                            }
                        }else if(index!=0){
                            list.add(name);
                        }
                        super.visitLocalVariable(name, description, signature, start, end, index);
                    }
                };
                return mv;
            }
        }, 0);
        return map;
    }
    private static byte[] readClassFile(Class<?> clazz){
        String path = clazz.getName().replaceAll("[.]", "/")+".class";
        try(InputStream is = clazz.getClassLoader().getResourceAsStream(path)){
            ByteArrayOutputStream bo = new ByteArrayOutputStream();
            byte[] bytes = new byte[512];
            int len = -1;
            while((len = is.read(bytes))>=0){
                bo.write(bytes, 0, len);
            }
            return bo.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}
